#include <map>
#include "ClassDefinition.h"
#include "../Exceptions.h"
#include "../GeneralVisitor.h"

ClassDefinition::ClassDefinition(string iClassName,
                                 vector<FieldDeclaration*> iFields,
                                 vector<MethodDefinition*> iMethods,
                                 vector<string> iParentClassNames)
{
    fClassName = iClassName;
    fFields = iFields;
    fMethods = iMethods;
    fParentClassNames = iParentClassNames;
    fInheritanceResolved = false;

    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        (*it)->SetClassDef(this);
    }
}

ClassDefinition::~ClassDefinition()
{
    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        delete (*it);
    }

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        delete (*it);
    }

    fFields.clear();
    fMethods.clear();
}

void ClassDefinition::ResolveVariables()
{
    set<string> lFieldNames;

    // nejdriv overit, jestli se nahodou nejaky field nejmenuje "this" :-)
    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        if((*it)->GetVarName() == "this")
            throw DuplicateVarDeclarationException(this->fClassName, "this");

        lFieldNames.insert((*it)->GetVarName());
    }

    for(vector<FieldDeclaration*>::iterator it = fInheritedFields.begin(); it != fInheritedFields.end(); ++it)
    {
        if((*it)->GetVarName() == "this")
            throw DuplicateVarDeclarationException(this->fClassName, "this");

        lFieldNames.insert((*it)->GetVarName());
    }

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        (*it)->CheckVariables(lFieldNames, this->fClassName);
    }
}

void ClassDefinition::ResolveFunctionCalls(set<FunctionDefinition*>& iAllFunctions)
{
    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        (*it)->CheckFunctionCalls(iAllFunctions, fClassName + "::" + (*it)->GetVarName());
    }

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        (*it)->CheckFunctionCalls(iAllFunctions, fClassName + "::" + (*it)->GetName());
    }
}

vector<MethodDefinition*> ClassDefinition::GetConstructors()
{
    vector<MethodDefinition*> lResult;

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        if((*it)->GetName() == fClassName)
        {
            (*it)->SetConstructorFlag();
            lResult.push_back(*it);
        }
    }

    return lResult;
}

void ClassDefinition::ResolveClassConstructions(set<ClassDefinition*>& iAllClasses)
{
    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        (*it)->CheckClassConstructions(iAllClasses, fClassName + "::" + (*it)->GetVarName());
    }

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        (*it)->CheckClassConstructions(iAllClasses, fClassName + "::" + (*it)->GetName());
    }
}

void ClassDefinition::CheckMethods()
{
    map<string, set<int> > lMethods;

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        string lName = (*it)->GetName();
        int lParamCount = (*it)->GetParamCount();
        (*it)->SetClassDef(this);

        if(lMethods.find(lName) == lMethods.end())
        {
            // funkci s timhle jmenem jeste neznam
            // TODO: zavola se vubec konstruktor pro set<int>?
            lMethods[lName].insert(lParamCount);
        }
        else
        {
            // funkci s timhle jmenem uz mam definovanou... opatrne...
            if(lMethods[lName].find(lParamCount) != lMethods[lName].end())
            {
                // taaak a mam te
                throw AmbiguousMethodDefinitionException(this->fClassName, lName, lParamCount);
            }
            else
                lMethods[lName].insert(lParamCount);
        }
    }
}

bool ClassDefinition::FieldExists(string iFieldName)
{
    // nejdriv se podivej do vlastnich fieldu...
    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        if((*it)->GetVarName() == iFieldName)
            return true;
    }

    // ... a pak do zdedenych
    for(vector<FieldDeclaration*>::iterator it = fInheritedFields.begin(); it != fInheritedFields.end(); ++it)
    {
        if((*it)->GetVarName() == iFieldName)
            return true;
    }

    return false;
}

bool ClassDefinition::MethodExists(string iMethodName, int iParamCount)
{
    MethodDefinition* lDef = this->GetMethodDefinition(iMethodName, iParamCount);

    if(lDef != 0)
        return true;
    else
        return false;
}

MethodDefinition* ClassDefinition::GetMethodDefinition(string iMethodName, unsigned int iParamCount)
{
    // nejdriv moje vlastni...
    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        if((*it)->GetName() == iMethodName && (*it)->GetParamCount() == iParamCount)
            return *it;
    }

    // ... pak zdedene
    for(vector<MethodDefinition*>::iterator it = fInheritedMethods.begin(); it != fInheritedMethods.end(); ++it)
    {
        if((*it)->GetName() == iMethodName && (*it)->GetParamCount() == iParamCount)
            return *it;
    }

    return 0;
}

void ClassDefinition::CheckExpressionTypes()
{
    set<string> lFieldNames;

    for(vector<FieldDeclaration*>::iterator it = fFields.begin(); it != fFields.end(); ++it)
    {
        lFieldNames.insert((*it)->GetVarName());
    }

    for(vector<FieldDeclaration*>::iterator it = fInheritedFields.begin(); it != fInheritedFields.end(); ++it)
    {
        lFieldNames.insert((*it)->GetVarName());
    }

    for(vector<MethodDefinition*>::iterator it = fMethods.begin(); it != fMethods.end(); ++it)
    {
        (*it)->CheckExpressionTypes(lFieldNames, fClassName);
    }
}

void ClassDefinition::ResolveInheritance(set<ClassDefinition*>& iAllClasses)
{
    this->SetDirectParents(iAllClasses);

    // 2. faze: vyres dedicnost rodicu a zkopiruj si od nich metody a fieldy
    for(vector<ClassDefinition*>::iterator it = fDirectParents.begin(); it != fDirectParents.end(); ++it)
    {
        if(!((*it)->IsInheritanceResolved()))
        {
            (*it)->ResolveInheritance(iAllClasses);
        }

        // tak... vem si od rodice jeho vlastni metody + metody, ktere zdedil, a uloz si je do svych zdedenych metod...
        vector<MethodDefinition*> lInheritedMethods = (*it)->GetAllMethods();
        this->ProcessInheritedMethods(lInheritedMethods);

        // ted to same pro fieldy...
        vector<FieldDeclaration*> lInheritedFields = (*it)->GetAllFields();
        this->ProcessInheritedFields(lInheritedFields);
    }

    this->fInheritanceResolved = true;
}

vector<FieldDeclaration*> ClassDefinition::GetAllFields()
{
    vector<FieldDeclaration*> lResult;

    lResult.insert(lResult.end(), this->fInheritedFields.begin(), this->fInheritedFields.end());
    lResult.insert(lResult.end(), this->fFields.begin(), this->fFields.end());

    return lResult;
}

vector<MethodDefinition*> ClassDefinition::GetAllMethods()
{
    vector<MethodDefinition*> lResult;

    lResult.insert(lResult.end(), this->fInheritedMethods.begin(), this->fInheritedMethods.end());
    lResult.insert(lResult.end(), this->fMethods.begin(), this->fMethods.end());

    return lResult;
}

void ClassDefinition::SetDirectParents(set<ClassDefinition*>& iAllClasses)
{
    map<string, ClassDefinition*> lAllClasses;
    map<string, ClassDefinition*>::iterator lClassesIterator;

    for(set<ClassDefinition*>::iterator it = iAllClasses.begin(); it != iAllClasses.end(); ++it)
    {
        lAllClasses[(*it)->GetClassName()] = *it;
    }

    // 1. faze: kontrola, ze rodicovska trida existuje, zaroven si ty tridy ulozim pro dalsi zpracovani
    for(vector<string>::iterator it = fParentClassNames.begin(); it != fParentClassNames.end(); ++it)
    {
        lClassesIterator = lAllClasses.find(*it);

        // lookup tridy... kdyz neexistuje, hod vyjimku
        if(lClassesIterator == lAllClasses.end())
        {
            throw BaseClassUndefinedException(fClassName, *it);
        }

        fDirectParents.push_back((*lClassesIterator).second);
    }
}

void ClassDefinition::ProcessInheritedMethods(vector<MethodDefinition*>& iMethods)
{
    // ted to projdi a zkontroluj, jestli tim nevznikne nejaky konflikt...
    // Hm... co pretezovani? Navrh: trida muze pretizit jakoukoliv metodu od predka.
    // Pri volani metody se nejdriv kouknu do svych vlastnich, a kdyz to nenajdu tam, tak se kouknu do zdedenych.
    // Aktualne ale nemame zpusob, jak zavolat primo tu rodicovskou - otazka je, jestli to vadi. Me teda ne :-)
    // Jediny problem, ktery muze nastat, je zdedeni dvou metod se stejnym nazvem a poctem parametru.
    // Zaroven si nebudu pamatovat zdedene metody, ktere ja pretezuji.

    for(vector<MethodDefinition*>::iterator it = iMethods.begin(); it != iMethods.end(); ++it)
    {
        MethodDefinition* lNewMethod = *it;

        for(vector<MethodDefinition*>::iterator it2 = fInheritedMethods.begin(); it2 != fInheritedMethods.end(); ++it2)
        {
            MethodDefinition* lAlreadyInheritedMethod = *it2;

            if(lNewMethod->GetName() == lAlreadyInheritedMethod->GetName()
                && lNewMethod->GetParamCount() == lAlreadyInheritedMethod->GetParamCount())
            {
                string lParentClass1 = lAlreadyInheritedMethod->GetClassDef()->GetClassName();
                string lParentClass2 = lNewMethod->GetClassDef()->GetClassName();
                throw InheritedMethodConflictException(fClassName, lNewMethod->GetName(), lParentClass1, lParentClass2);
            }
        }

        bool lCanAdd = true;

        for(vector<MethodDefinition*>::iterator it2 = fMethods.begin(); it2 != fMethods.end(); ++it2)
        {
            if((*it2)->GetName() == lNewMethod->GetName() && (*it2)->GetParamCount() == lNewMethod->GetParamCount())
            {
                lCanAdd = false;
                break;
            }
        }

        if(lCanAdd)
            this->fInheritedMethods.push_back(lNewMethod);
    }
}

void ClassDefinition::ProcessInheritedFields(vector<FieldDeclaration*>& iFields)
{
    // projdi zdedene fieldy...
    for(vector<FieldDeclaration*>::iterator it = iFields.begin(); it != iFields.end(); ++it)
    {
        FieldDeclaration* lNewField = *it;

        // ... a zkontroluj, jestli field stejneho jmena jiz neni zdedeny od nejakeho jineho predka
        for(vector<FieldDeclaration*>::iterator it2 = fInheritedFields.begin(); it2 != fInheritedFields.end(); ++it2)
        {
            FieldDeclaration* lAlreadyInheritedField = *it2;

            if(lNewField->GetVarName() == lAlreadyInheritedField->GetVarName())
            {
                string lParentClass1 = lAlreadyInheritedField->GetClassDef()->GetClassName();
                string lParentClass2 = lNewField->GetClassDef()->GetClassName();
                throw InheritedFieldConflictException(fClassName, lNewField->GetVarName(), lParentClass1, lParentClass2);
            }
        }

        // ... a taky zkontroluj, jestli nahodou nemam ja sam nejaky field, ktery se jmenuje stejne
        for(vector<FieldDeclaration*>::iterator it2 = fFields.begin(); it2 != fFields.end(); ++it2)
        {
            if(lNewField->GetVarName() == (*it2)->GetVarName())
            {
                // Houstone mame problem
                throw FieldOverloadingException(fClassName, lNewField->GetClassDef()->GetClassName(), lNewField->GetVarName());
            }
        }
    }

    this->fInheritedFields.insert(this->fInheritedFields.end(), iFields.begin(), iFields.end());
}

void ClassDefinition::Accept(GeneralVisitor* iVisitor)
{
    iVisitor->Visit(this);
}